﻿using System;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;
using Kyoh.App.Renamer.RenameEventArgs;
using System.Collections.Generic;
using System.Diagnostics;

namespace Kyoh.App.Renamer
{
    internal partial class WorkingDialog : Form
    {
        private readonly object _cancel = new object();
        private readonly object _confirming = new object();
        private readonly AutoResetEvent _confirmed = new AutoResetEvent(false);

        private readonly RenameWorker _worker;
        private readonly bool _eventAdded;

        private bool _skipDupAll;
        private bool _skipFailAll;
        private bool _overwriteAll;

        private WorkingDialog(RenameWorker worker)
        {
            InitializeComponent();
            confirmPictureBox.Image = SystemIcons.Asterisk.ToBitmap();

            _worker = worker;
            _worker.RenameProgress += RenameProgress;
            _worker.RenameCompleted += RenameCompleted;
            _worker.RenameFailed += RenameFailed;
            _worker.DuplicatedPath += DuplicatedPath;
            //TODO: _worker.WorkFailed += ...
            _eventAdded = true;
            progressBar1.Maximum = worker.GetFilteredCount();
            progressBar1.Minimum = 0;
        }

        private void RenameProgress(object sender, RenameProgressChangedEventArgs e)
        {
            Invoke(() =>
                       {
                           directoryPathLabel.Text = e.CurrentPath;
                           basenamePictureBox.Image = e.BeforeName.Icon;
                           basenameLabel.Text = e.BeforeName.Text;
                           dataGridView1.Rows.Insert(0, new[] {e.CurrentPath, e.BeforeName.Text, e.Destination});
                           progressBar1.Value = Math.Min(progressBar1.Maximum, progressBar1.Value + 1);
                       });
        }

        private void DuplicatedPath(object sender, DuplicatedPathEventArgs e)
        {
            if (_skipDupAll)
                e.Method = DuplicatedPathEventArgs.DuplicatedPathMethod.Skip;
            else if (_overwriteAll)
                e.Method = DuplicatedPathEventArgs.DuplicatedPathMethod.OverWrite;
            else if (!BeginConfirm(Properties.TextResources.DuplicatedPath, true, e.OldPath, e.NewPath))
                EndConfirm();
            else if (skipAllRadioButton.Checked)
            {
                _skipDupAll = true;
                e.Method = DuplicatedPathEventArgs.DuplicatedPathMethod.Skip;
            }
            else if (overwriteAllRadioButton.Checked)
            {
                _overwriteAll = true;
                e.Method = DuplicatedPathEventArgs.DuplicatedPathMethod.OverWrite;
            }
            else
            {
                e.Method = appendNumbersRadioButton.Checked
                               ? DuplicatedPathEventArgs.DuplicatedPathMethod.Number
                               : retryRadioButton.Checked
                                     ? DuplicatedPathEventArgs.DuplicatedPathMethod.Retry
                                     : overwriteRadioButton.Checked
                                           ? DuplicatedPathEventArgs.DuplicatedPathMethod.OverWrite
                                           : DuplicatedPathEventArgs.DuplicatedPathMethod.Skip;
            }

            if (e.Method != DuplicatedPathEventArgs.DuplicatedPathMethod.Retry)
                Invoke(() => progressBar1.Value++);
        }

        private void RenameFailed(object sender, RenameFailedEventArgs e)
        {
            if (_skipFailAll)
                e.Method = RenameFailedEventArgs.RenameResultMethod.Skip;
            else if (!BeginConfirm(e.Message, true, e.OldPath, e.NewPath))
                EndConfirm();
            else if (skipAllRadioButton.Checked)
            {
                _skipFailAll = true;
                e.Method = RenameFailedEventArgs.RenameResultMethod.Skip;
            }
            else
            {
                e.Method = retryRadioButton.Checked
                               ? RenameFailedEventArgs.RenameResultMethod.Retry
                               : RenameFailedEventArgs.RenameResultMethod.Skip;
            }

            if (e.Method != RenameFailedEventArgs.RenameResultMethod.Retry)
                Invoke(() => progressBar1.Value++);
        }

        private void RenameCompleted(object sender, CompletedEventArgs e)
        {
            lock (_cancel)
            {
                Invoke(() =>
                {
                    if (Properties.Settings.Default.ProgressAutoClosing)
                        Close();
                    else
                    {
                        completedButton.Visible = true;
                        abortLabel.Visible =
                        abortButton.Visible =
                        abortButton.Enabled =
                        cancelLabel.Visible =
                        cancelButton.Visible =
                        cancelButton.Enabled = false;
                    }
                });
            }
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            if (_eventAdded)
            {
                _worker.RenameProgress -= RenameProgress;
                _worker.RenameCompleted -= RenameCompleted;
                _worker.RenameFailed -= RenameFailed;
                _worker.DuplicatedPath -= DuplicatedPath;
            }
            base.OnFormClosing(e);
        }

        protected override void OnShown(EventArgs e)
        {
            _worker.Rename();
            base.OnShown(e);
        }

        public static void ShowDialog(MainForm mainForm, RenameWorker worker)
        {
            using (var dialog = new WorkingDialog(worker))
                dialog.ShowDialog(mainForm);
        }

        private bool BeginConfirm(string message, bool duplicated, string oldPath, string newPath)
        {
            Debug.WriteLine("entering to confirming");
            //Invoke(() => Monitor.Enter(_confirming));
            Monitor.Enter(_confirming);
            try
            {
                Invoke(() =>
                           {
                               ShowConfirm();
                               messageLabel.Text = message;
                               targetLabel.Text = oldPath;
                               newNameLabel.Text = newPath;

                               retryRadioButton.Checked = true;
                               appendNumbersRadioButton.Enabled =
                                   appendNumbersRadioButton.Visible =
                                   overwriteAllRadioButton.Enabled =
                                   overwriteAllRadioButton.Visible =
                                   overwriteRadioButton.Enabled =
                                   overwriteRadioButton.Visible = duplicated;
                           });

                // OKボタン待機
                _confirmed.WaitOne();
                return true;
            }
            catch
            {
                //Invoke(() => Monitor.Exit(_confirming));
                Monitor.Exit(_confirming);
                Debug.WriteLine("exited from confirming");
                return false;
            }
        }

        private void EndConfirm()
        {
            try
            {
                Invoke(() =>
                           {
                               HideConfirm();
                           });
                _confirmed.Set();
            }
            finally
            {
                try
                {
                    //Invoke(() => Monitor.Exit(_confirming));
                    Monitor.Exit(_confirming);
                    Debug.WriteLine("exited confirming : EndConfirm");
                }
                catch(SynchronizationLockException)
                {
                }
            }
        }

        private void Invoke(Action action)
        {
            if (InvokeRequired)
                base.Invoke(action);
            else
                action.Invoke();
        }

        private void ShowConfirm()
        {
            confirmRadioButton.Visible =
            confirmRadioButton.Checked = true;
            progressRadioButton.Checked = false;
            confirmTableLayoutPanel.Enabled =
                confirmTableLayoutPanel.Visible = true;
            progressTableLayoutPanel.Enabled =
                progressTableLayoutPanel.Visible = false;
        }

        private void HideConfirm()
        {
            confirmRadioButton.Visible =
            confirmRadioButton.Checked = false;
            progressRadioButton.Checked = true;
            confirmTableLayoutPanel.Enabled =
                confirmTableLayoutPanel.Visible = false;
            progressTableLayoutPanel.Enabled =
                progressTableLayoutPanel.Visible = true;
        }

        private void ShowProgress()
        {
            confirmRadioButton.Checked = false;
            progressRadioButton.Checked = true;
            confirmTableLayoutPanel.Enabled =
                confirmTableLayoutPanel.Visible = false;
            progressTableLayoutPanel.Enabled =
                progressTableLayoutPanel.Visible = true;
        }

        private void confirmRadioButton_Click(object sender, EventArgs e)
        {
            ShowConfirm();
        }

        private void progressRadioButton_Click(object sender, EventArgs e)
        {
            ShowProgress();
        }

        private void ConfirmRadioButtonCheckedChanged(object sender, EventArgs e)
        {
        }

        private void ProgressRadioButtonCheckedChanged(object sender, EventArgs e)
        {
        }

        private void OKButtonClick(object sender, EventArgs e)
        {
            EndConfirm();
        }

        private void AbortButtonClick(object sender, EventArgs e)
        {
            new Action(() => _worker.CancelRename()).BeginInvoke(null, null);
            EndConfirm();
        }

        private void CancelButtonClick(object sender, EventArgs e)
        {
            lock (_cancel)
            {
                new Action(() => _worker.CancelRename()).BeginInvoke(null, null);
                EndConfirm();
                _worker.Undo();
            }
        }

        private void finishedButton_Click(object sender, EventArgs e)
        {
            Close();
        }

    }
}